/**
 * @file socket_server.c
 * @author He Chuanxi (you@domain.com)
 * @brief 一个简单的 TCP服务器应用程序示例代码
 * @version 0.1
 * @date 2023-07-03
 * 
 * @copyright Copyright (c) 2023
 * 
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>

#define SERVER_PORT 8888 /* 注意端口号不能冲突，不常用的通常大于 5000 */

int main(int argc, char const *argv[])
{
    struct sockaddr_in server_address = { 0 };
    struct sockaddr_in client_address = { 0 };
    char ip_str[20] = { 0 };
    int sockfd, connfd;
    int addrlen = sizeof(client_address);
    char recvbuf[512];
    int ret;

    /* 打开套接字,得到套接字描述符 */
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (0 > sockfd) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }
    /* 将套接字与指定端口号进行绑定 */
    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = htons(SERVER_PORT);
    ret = bind(sockfd, (struct sockaddr *)&server_address, sizeof(server_address));
    if (0 > ret) {
        perror("bind failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    /* 使服务器进入监听状态 */
    ret = listen(sockfd, 50);
    if (0 > ret) {
        perror("listen failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    /* 阻塞等待客户端连接 */
    connfd = accept(sockfd, (struct sockaddr*)&client_address, &addrlen);
    if (0 > connfd) {
        perror("accept() error");
        close(sockfd);
        exit(EXIT_FAILURE);
    }
    printf("有客户端接入...\n");
    inet_ntop(AF_INET, &client_address.sin_addr, ip_str, sizeof(ip_str));
    printf("客户端主机的IP地址: %s\n", ip_str);
    printf("客户端进程的端口号： %d\n", client_address.sin_port);
    /* 接收客户端发送过来的数据 */
    for (;;) {
        /* 接收缓冲区清零 */
        memset(recvbuf, 0x0, sizeof(recvbuf));
        /* 读数据 */
        ret = recv(connfd, recvbuf, sizeof(recvbuf), 0);
        if (0 > ret) {
            perror("recv() error");
            close(connfd);
            break;
        }
        /* 将读取到的数据以字符串的形式打印出来 */
        printf("from client: %s\n", recvbuf);
        /* 如果读取到 "exit" 则关闭套接字退出程序 */
        if (0 == strncmp("exit", recvbuf, 4)) {
            printf("server exit...\n");
            close(connfd);
            break;
        }
    }
    /* 关闭套接字 */
    close(sockfd);
    exit(EXIT_SUCCESS);
}
